home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / wais / waisgate / HTAnchor.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  14KB  |  536 lines

  1. /*    Hypertext "Anchor" Object                HTAnchor.c
  2. **    ==========================
  3. **
  4. ** An anchor represents a region of a hypertext document which is linked to
  5. ** another anchor in the same or a different document.
  6. **
  7. ** History
  8. **
  9. **         Nov 1990  Written in Objective-C for the NeXT browser (TBL)
  10. **    24-Oct-1991 (JFG), written in C, browser-independant 
  11. **    21-Nov-1991 (JFG), first complete version
  12. **
  13. **    (c) Copyright CERN 1991 - See Copyright.html
  14. */
  15.  
  16. #define HASH_SIZE 101        /* Arbitrary prime. Memory/speed tradeoff */
  17.  
  18. #include <ctype.h>
  19. #include "tcp.h"
  20. #include "HTAnchor.h"
  21. #include "HTUtils.h"
  22. #include "HTParse.h"
  23.  
  24. typedef struct _HyperDoc Hyperdoc;
  25. #ifdef vms
  26. struct _HyperDoc {
  27.     int junk;    /* VMS cannot handle pointers to undefined structs */
  28. };
  29. #endif
  30.  
  31. PRIVATE HTList **adult_table=0;  /* Point to table of lists of all parents */
  32.  
  33. /*                Creation Methods
  34. **                ================
  35. **
  36. **    Do not use "new" by itself outside this module. In order to enforce
  37. **    consistency, we insist that you furnish more information about the
  38. **    anchor you are creating : use newWithParent or newWithAddress.
  39. */
  40.  
  41. PRIVATE HTParentAnchor * HTParentAnchor_new
  42.   NOARGS
  43. {
  44.   HTParentAnchor *newAnchor = 
  45.     (HTParentAnchor *) calloc (1, sizeof (HTParentAnchor));  /* zero-filled */
  46.   newAnchor->parent = newAnchor;
  47.   return newAnchor;
  48. }
  49.  
  50. PRIVATE HTChildAnchor * HTChildAnchor_new
  51.   NOARGS
  52. {
  53.   return (HTChildAnchor *) calloc (1, sizeof (HTChildAnchor));  /* zero-filled */
  54. }
  55.  
  56.  
  57. /*    Case insensitive string comparison
  58. **    ----------------------------------
  59. ** On entry,
  60. **    s    Points to one string, null terminated
  61. **    t    points to the other.
  62. ** On exit,
  63. **    returns    YES if the strings are equivalent ignoring case
  64. **        NO if they differ in more than  their case.
  65. */
  66.  
  67. PRIVATE BOOL equivalent
  68.   ARGS2 (CONST char *,s, CONST char *,t)
  69. {
  70.   if (s && t) {  /* Make sure they point to something */
  71.     for ( ; *s && *t ; s++, t++) {
  72.         if (TOUPPER(*s) != TOUPPER(*t))
  73.       return NO;
  74.     }
  75.     return TOUPPER(*s) == TOUPPER(*t);
  76.   } else
  77.     return s == t;  /* Two NULLs are equivalent, aren't they ? */
  78. }
  79.  
  80.  
  81. /*    Create new or find old sub-anchor
  82. **    ---------------------------------
  83. **
  84. **    Me one is for a new anchor being edited into an existing
  85. **    document. The parent anchor must already exist.
  86. */
  87.  
  88. PUBLIC HTChildAnchor * HTAnchor_findChild
  89.   ARGS2 (HTParentAnchor *,parent, CONST char *,tag)
  90. {
  91.   HTChildAnchor *child;
  92.   HTList *kids;
  93.  
  94.   if (! parent) {
  95.     if (TRACE) printf ("HTAnchor_findChild called with NULL parent.\n");
  96.     return NULL;
  97.   }
  98.   if (kids = parent->children) {  /* parent has children : search them */
  99.     if (tag && *tag) {        /* TBL */
  100.     while (child = HTList_nextObject (kids)) {
  101.         if (equivalent(child->tag, tag)) { /* Case sensitive 920226 */
  102.         if (TRACE) fprintf (stderr,
  103.            "Child anchor %p of parent %p with name `%s' already exists.\n",
  104.             (void*)child, (void*)parent, tag);
  105.         return child;
  106.         }
  107.     }
  108.      }  /*  end if tag is void */
  109.   } else  /* parent doesn't have any children yet : create family */
  110.     parent->children = HTList_new ();
  111.  
  112.   child = HTChildAnchor_new ();
  113.   if (TRACE) fprintf(stderr, "new Anchor %p named `%s' is child of %p\n",
  114.        (void*)child, (int)tag ? tag : (CONST char *)"" , (void*)parent); /* int for apollo */
  115.   HTList_addObject (parent->children, child);
  116.   child->parent = parent;
  117.   StrAllocCopy(child->tag, tag);
  118.   return child;
  119. }
  120.  
  121.  
  122. /*    Create or find a child anchor with a possible link
  123. **    --------------------------------------------------
  124. **
  125. **    Create new anchor with a given parent and possibly
  126. **    a name, and possibly a link to a _relatively_ named anchor.
  127. **    (Code originally in ParseHTML.h)
  128. */
  129. PUBLIC HTChildAnchor * HTAnchor_findChildAndLink
  130.   ARGS4(
  131.        HTParentAnchor *,parent,    /* May not be 0 */
  132.        CONST char *,tag,    /* May be "" or 0 */
  133.        CONST char *,href,    /* May be "" or 0 */
  134.        HTLinkType *,ltype    /* May be 0 */
  135.        )
  136. {
  137.   HTChildAnchor * child = HTAnchor_findChild(parent, tag);
  138.   if (href && *href) {
  139.     char * relative_to = HTAnchor_address((HTAnchor *) parent);
  140.     char * parsed_address = HTParse(href, relative_to, PARSE_ALL);
  141.     HTAnchor * dest = HTAnchor_findAddress(parsed_address);
  142.     HTAnchor_link((HTAnchor *) child, dest, ltype);
  143.     free(parsed_address);
  144.     free(relative_to);
  145.   }
  146.   return child;
  147. }
  148.  
  149.  
  150. /*    Create new or find old named anchor
  151. **    -----------------------------------
  152. **
  153. **    Me one is for a reference which is found in a document, and might
  154. **    not be already loaded.
  155. **    Note: You are not guaranteed a new anchor -- you might get an old one,
  156. **    like with fonts.
  157. */
  158.  
  159. HTAnchor * HTAnchor_findAddress
  160.   ARGS1 (CONST char *,address)
  161. {
  162.   char *tag = HTParse (address, "", PARSE_ANCHOR);  /* Anchor tag specified ? */
  163.  
  164.   /* If the address represents a sub-anchor, we recursively load its parent,
  165.      then we create a child anchor within that document. */
  166.   if (*tag) {
  167.     char *docAddress = HTParse(address, "", PARSE_ACCESS | PARSE_HOST |
  168.                                 PARSE_PATH | PARSE_PUNCTUATION);
  169.     HTParentAnchor * foundParent =
  170.       (HTParentAnchor *) HTAnchor_findAddress (docAddress);
  171.     HTChildAnchor * foundAnchor = HTAnchor_findChild (foundParent, tag);
  172.     free (docAddress);
  173.     free (tag);
  174.     return (HTAnchor *) foundAnchor;
  175.   }
  176.   
  177.   else { /* If the address has no anchor tag, 
  178.         check whether we have this node */
  179.     int hash;
  180.     CONST char *p;
  181.     HTList * adults;
  182.     HTList *grownups;
  183.     HTParentAnchor * foundAnchor;
  184.  
  185.     free (tag);
  186.     
  187.     /* Select list from hash table */
  188.     for(p=address, hash=0; *p; p++)
  189.         hash = (hash * 3 + (*(unsigned char*)p))
  190.          % HASH_SIZE;
  191.     if (!adult_table)
  192.         adult_table = (HTList**) calloc(HASH_SIZE, sizeof(HTList*));
  193.     if (!adult_table[hash]) adult_table[hash] = HTList_new();
  194.     adults = adult_table[hash];
  195.  
  196.     /* Search list for anchor */
  197.     grownups = adults;
  198.     while (foundAnchor = HTList_nextObject (grownups)) {
  199.        if (equivalent(foundAnchor->address, address)) {
  200.     if (TRACE) fprintf(stderr, "Anchor %p with address `%s' already exists.\n",
  201.               (void*) foundAnchor, address);
  202.     return (HTAnchor *) foundAnchor;
  203.       }
  204.     }
  205.     
  206.     /* Node not found : create new anchor */
  207.     foundAnchor = HTParentAnchor_new ();
  208.     if (TRACE) fprintf(stderr, "New anchor %p has hash %d and address `%s'\n",
  209.         (void*)foundAnchor, hash, address);
  210.     StrAllocCopy(foundAnchor->address, address);
  211.     HTList_addObject (adults, foundAnchor);
  212.     return (HTAnchor *) foundAnchor;
  213.   }
  214. }
  215.  
  216.  
  217. /*    Delete an anchor and possibly related things (auto garbage collection)
  218. **    --------------------------------------------
  219. **
  220. **    The anchor is only deleted if the corresponding document is not loaded.
  221. **    All outgoing links from parent and children are deleted, and this anchor
  222. **    is removed from the sources list of all its targets.
  223. **    We also try to delete the targets whose documents are not loaded.
  224. **    If this anchor's source list is empty, we delete it and its children.
  225. */
  226.  
  227. PRIVATE void deleteLinks
  228.   ARGS1 (HTAnchor *,me)
  229. {
  230.   if (! me)
  231.     return;
  232.  
  233.   /* Recursively try to delete target anchors */
  234.   if (me->mainLink.dest) {
  235.     HTParentAnchor *parent = me->mainLink.dest->parent;
  236.     HTList_removeObject (parent->sources, me);
  237.     if (! parent->document)  /* Test here to avoid calling overhead */
  238.       HTAnchor_delete (parent);
  239.   }
  240.   if (me->links) {  /* Extra destinations */
  241.     HTLink *target;
  242.     while (target = HTList_removeLastObject (me->links)) {
  243.       HTParentAnchor *parent = target->dest->parent;
  244.       HTList_removeObject (parent->sources, me);
  245.       if (! parent->document)  /* Test here to avoid calling overhead */
  246.     HTAnchor_delete (parent);
  247.     }
  248.   }
  249. }
  250.  
  251. PUBLIC BOOL HTAnchor_delete
  252.   ARGS1 (HTParentAnchor *,me)
  253. {
  254.   HTChildAnchor *child;
  255.  
  256.   /* Don't delete if document is loaded */
  257.   if (me->document)
  258.     return NO;
  259.  
  260.   /* Recursively try to delete target anchors */
  261.   deleteLinks ((HTAnchor *) me);
  262.  
  263.   if (! HTList_isEmpty (me->sources)) {  /* There are still incoming links */
  264.     /* Delete all outgoing links from children, if any */
  265.     HTList *kids = me->children;
  266.     while (child = HTList_nextObject (kids))
  267.       deleteLinks ((HTAnchor *) child);
  268.     return NO;  /* Parent not deleted */
  269.   }
  270.  
  271.   /* No more incoming links : kill everything */
  272.   /* First, recursively delete children */
  273.   while (child = HTList_removeLastObject (me->children)) {
  274.     deleteLinks ((HTAnchor *) child);
  275.     free (child->tag);
  276.     free (child);
  277.   }
  278.  
  279.   /* Now kill myself */
  280.   HTList_delete (me->children);
  281.   HTList_delete (me->sources);
  282.   free (me->address);
  283.   /* Devise a way to clean out the HTFormat if no longer needed (ref count?) */
  284.   free (me);
  285.   return YES;  /* Parent deleted */
  286. }
  287.  
  288.  
  289. /*        Move an anchor to the head of the list of its siblings
  290. **        ------------------------------------------------------
  291. **
  292. **    This is to ensure that an anchor which might have already existed
  293. **    is put in the correct order as we load the document.
  294. */
  295.  
  296. void HTAnchor_makeLastChild
  297.   ARGS1(HTChildAnchor *,me)
  298. {
  299.   if (me->parent != (HTParentAnchor *) me) {  /* Make sure it's a child */
  300.     HTList * siblings = me->parent->children;
  301.     HTList_removeObject (siblings, me);
  302.     HTList_addObject (siblings, me);
  303.   }
  304. }
  305.  
  306. /*    Data access Functions
  307. **    ---------------------
  308. */
  309.  
  310. PUBLIC HTParentAnchor * HTAnchor_parent
  311.   ARGS1 (HTAnchor *,me)
  312. {
  313.   return me ? me->parent : NULL;
  314. }
  315.  
  316. void HTAnchor_setDocument
  317.   ARGS2 (HTParentAnchor *,me, HyperDoc *,doc)
  318. {
  319.   if (me)
  320.     me->document = doc;
  321. }
  322.  
  323. HyperDoc * HTAnchor_document
  324.   ARGS1 (HTParentAnchor *,me)
  325. {
  326.   return me ? me->document : NULL;
  327. }
  328.  
  329.  
  330. /* We don't want code to change an address after anchor creation... yet ?
  331. void HTAnchor_setAddress
  332.   ARGS2 (HTAnchor *,me, char *,addr)
  333. {
  334.   if (me)
  335.     StrAllocCopy (me->parent->address, addr);
  336. }
  337. */
  338.  
  339. char * HTAnchor_address
  340.   ARGS1 (HTAnchor *,me)
  341. {
  342.   char *addr = NULL;
  343.   if (me) {
  344.     if (((HTParentAnchor *) me == me->parent) ||
  345.         !((HTChildAnchor *) me)->tag) {  /* it's an adult or no tag */
  346.       StrAllocCopy (addr, me->parent->address);
  347.     }
  348.     else {  /* it's a named child */
  349.       addr = malloc (2 + strlen (me->parent->address)
  350.              + strlen (((HTChildAnchor *) me)->tag));
  351.       if (addr == NULL) outofmem(__FILE__, "HTAnchor_address");
  352.       sprintf (addr, "%s#%s", me->parent->address,
  353.            ((HTChildAnchor *) me)->tag);
  354.     }
  355.   }
  356.   return addr;
  357. }
  358.  
  359.  
  360.  
  361. void HTAnchor_setFormat
  362.   ARGS2 (HTParentAnchor *,me, HTFormat ,form)
  363. {
  364.   if (me)
  365.     me->format = form;
  366. }
  367.  
  368. HTFormat HTAnchor_format
  369.   ARGS1 (HTParentAnchor *,me)
  370. {
  371.   return me ? me->format : NULL;
  372. }
  373.  
  374.  
  375.  
  376. void HTAnchor_setIndex
  377.   ARGS1 (HTParentAnchor *,me)
  378. {
  379.   if (me)
  380.     me->isIndex = YES;
  381. }
  382.  
  383. BOOL HTAnchor_isIndex
  384.   ARGS1 (HTParentAnchor *,me)
  385. {
  386.   return me ? me->isIndex : NO;
  387. }
  388.  
  389.  
  390.  
  391. BOOL HTAnchor_hasChildren
  392.   ARGS1 (HTParentAnchor *,me)
  393. {
  394.   return me ? ! HTList_isEmpty(me->children) : NO;
  395. }
  396.  
  397. /*    Title handling
  398. */
  399. CONST char * HTAnchor_title
  400.   ARGS1 (HTParentAnchor *,me)
  401. {
  402.   return me ? me->title : 0;
  403. }
  404.  
  405. void HTAnchor_setTitle
  406.   ARGS2(HTParentAnchor *,me, CONST char *,title)
  407. {
  408.   StrAllocCopy(me->title, title);
  409. }
  410.  
  411. void HTAnchor_appendTitle
  412.   ARGS2(HTParentAnchor *,me, CONST char *,title)
  413. {
  414.   StrAllocCat(me->title, title);
  415. }
  416.  
  417. /*    Link me Anchor to another given one
  418. **    -------------------------------------
  419. */
  420.  
  421. BOOL HTAnchor_link
  422.   ARGS3(HTAnchor *,source, HTAnchor *,destination, HTLinkType *,type)
  423. {
  424.   if (! (source && destination))
  425.     return NO;  /* Can't link to/from non-existing anchor */
  426.   if (TRACE) printf ("Linking anchor %p to anchor %p\n", source, destination);
  427.   if (! source->mainLink.dest) {
  428.     source->mainLink.dest = destination;
  429.     source->mainLink.type = type;
  430.   } else {
  431.     HTLink * newLink = (HTLink *) malloc (sizeof (HTLink));
  432.     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_link");
  433.     newLink->dest = destination;
  434.     newLink->type = type;
  435.     if (! source->links)
  436.       source->links = HTList_new ();
  437.     HTList_addObject (source->links, newLink);
  438.   }
  439.   if (!destination->parent->sources)
  440.     destination->parent->sources = HTList_new ();
  441.   HTList_addObject (destination->parent->sources, source);
  442.   return YES;  /* Success */
  443. }
  444.  
  445.  
  446. /*    Manipulation of links
  447. **    ---------------------
  448. */
  449.  
  450. HTAnchor * HTAnchor_followMainLink
  451.   ARGS1 (HTAnchor *,me)
  452. {
  453.   return me->mainLink.dest;
  454. }
  455.  
  456. HTAnchor * HTAnchor_followTypedLink
  457.   ARGS2 (HTAnchor *,me, HTLinkType *,type)
  458. {
  459.   if (me->mainLink.type == type)
  460.     return me->mainLink.dest;
  461.   if (me->links) {
  462.     HTList *links = me->links;
  463.     HTLink *link;
  464.     while (link = HTList_nextObject (links))
  465.       if (link->type == type)
  466.     return link->dest;
  467.   }
  468.   return NULL;  /* No link of me type */
  469. }
  470.  
  471.  
  472. /*    Make main link
  473. */
  474. BOOL HTAnchor_makeMainLink
  475.   ARGS2 (HTAnchor *,me, HTLink *,movingLink)
  476. {
  477.   /* Check that everything's OK */
  478.   if (! (me && HTList_removeObject (me->links, movingLink)))
  479.     return NO;  /* link not found or NULL anchor */
  480.   else {
  481.     /* First push current main link onto top of links list */
  482.     HTLink *newLink = (HTLink*) malloc (sizeof (HTLink));
  483.     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_makeMainLink");
  484.     memcpy (newLink, & me->mainLink, sizeof (HTLink));
  485.     HTList_addObject (me->links, newLink);
  486.  
  487.     /* Now make movingLink the new main link, and free it */
  488.     memcpy (& me->mainLink, movingLink, sizeof (HTLink));
  489.     free (movingLink);
  490.     return YES;
  491.   }
  492. }
  493.  
  494.  
  495. /*    Methods List
  496. **    ------------
  497. */
  498.  
  499. PUBLIC HTList * HTAnchor_methods ARGS1(HTParentAnchor *, me)
  500. {
  501.     if (!me->methods) {
  502.         me->methods = HTList_new();
  503.     }
  504.     return me->methods;
  505. }
  506.  
  507. /*    Protocol
  508. **    --------
  509. */
  510.  
  511. PUBLIC void * HTAnchor_protocol ARGS1(HTParentAnchor *, me)
  512. {
  513.     return me->protocol;
  514. }
  515.  
  516. PUBLIC void HTAnchor_setProtocol ARGS2(HTParentAnchor *, me,
  517.     void*,    protocol)
  518. {
  519.     me->protocol = protocol;
  520. }
  521.  
  522. /*    Physical Address
  523. **    ----------------
  524. */
  525.  
  526. PUBLIC char * HTAnchor_physical ARGS1(HTParentAnchor *, me)
  527. {
  528.     return me->physical;
  529. }
  530.  
  531. PUBLIC void HTAnchor_setPhysical ARGS2(HTParentAnchor *, me,
  532.     char *,    physical)
  533. {
  534.     StrAllocCopy(me->physical, physical);
  535. }
  536.